#include "C_funcs.h"
#include "dir.h"
#include "funcHdr.h"

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//------------------------------------------------------------------------------
void  hdrOutput (void) ;

//------------------------------------------------------------------------------
#if 0
#	define LOGF(...) printf(__VA_ARGS__)
#else
#	define LOGF(...)
#endif

//------------------------------------------------------------------------------
typedef
	enum State {
		stCode, stHeader, stFunc, stDesc, stArgs, stRetn, stNote, stAuth,
	}
State;

//------------------------------------------------------------------------------
#define  MAX_L  (256)       // Longest line in a source file

//------------------------------------------------------------------------------
static  char   *fname = NULL;
static  State  hstate = stHeader;

Header  hdr;

//+=============================================================================
void  hdrAdd (char* l)
{
	char  *s, *cp;
	int   len, max;

	char  *lp = l + 2;  // Skip the "//" prefix

	strim(lp);
	if (!*lp)  return ;

	if      (strncasecmp(lp, "func:", 5) == 0)  { hstate = stFunc;  lp += 5; }
	else if (strncasecmp(lp, "desc:", 5) == 0)  { hstate = stDesc;  lp += 5; }
	else if (strncasecmp(lp, "args:", 5) == 0)  { hstate = stArgs;  lp += 4; }//4
	else if (strncasecmp(lp, "retn:", 5) == 0)  { hstate = stRetn;  lp += 4; }//4
	else if (strncasecmp(lp, "note:", 5) == 0)  { hstate = stNote;  lp += 4; }//4
	else if (strncasecmp(lp, "auth:", 5) == 0)  { hstate = stAuth;  lp += 5; }
	else if (strncmp    (lp, "=="   , 2) == 0)  { hstate = stHeader; }

	if (hstate == stFunc) {
		s = hdr.func;
		len = strlen(s);
		max = LEN_FUNC - len;
		strim(lp);
		if (len)  s[len++] = ' ' ;
		if (snprintf(s + len, max, "%s", lp) >= max)  strcpy(s + max - 4, "...") ;

	} else if (hstate == stDesc) {
		s = hdr.desc;
		len = strlen(s);
		max = LEN_DESC - len;
		strim(lp);
		if (len)  s[len++] = ' ' ;
		if (snprintf(s + len, max, "%s", lp) >= max)  strcpy(s + max - 4, "...") ;

	} else if ((hstate == stArgs) || (hstate == stRetn)) {
		int  arg = 0;

		if (hstate == stArgs)  arg = hdr.argc ;
		else                   arg = 0 ;

		if (lp[0] == ':') {

			if (hstate == stArgs)  arg = ++hdr.argc ;

			if ( (s = strstr(lp + 1, "..")) ) {
				cp = s;

				if (hstate == stArgs) {
					while (isspace(*--cp)) *cp = '\0' ;
					for (--cp;  !isspace(*cp) && (cp > lp);  cp--) ;
					cp++;
//					if (cp != lp)
						if (snprintf(hdr.argName[arg], LEN_ANAME, "%s", cp) >= LEN_ANAME)  
							strcpy(&hdr.argName[arg][LEN_ANAME - 4], "...") ;
				}

				while (isspace(*--cp)) *cp = '\0' ;
				for (--cp;  !isspace(*cp) && (cp > lp);  cp--) ;
				cp++;
//				if (cp != lp)
					if (snprintf(hdr.argType[arg], LEN_ATYPE, "%s", cp) >= LEN_ATYPE)  
						strcpy(&hdr.argType[arg][LEN_ATYPE - 4], "...") ;

				*cp = '\0';
				strim(lp + 1);
				if (snprintf(hdr.argQual[arg], LEN_AQUAL, "%s", lp + 1) >= LEN_AQUAL)  
					strcpy(&hdr.argQual[arg][LEN_AQUAL - 4], "...") ;

				lp = s + 2;

			} else {
				if (strstr(lp, "-none-")) {
					if (hstate == stArgs)  hdr.argc-- ;
					goto _return;
				}
			}
		}

		s = hdr.argDesc[arg];
		len = strlen(s);
		max = LEN_ADESC - len;
		strim(lp);
		if (len)  s[len++] = ' ' ;
		if (snprintf(s + len, max, "%s", lp) >= max)  strcpy(s + max - 4, "...") ;

	} else if (hstate == stNote) {
		bool  first = (strncasecmp(lp-4, "note:", 5) == 0) ? true : false ;
		bool  start = (lp[0] == ':') ? true : false ;

		if (start) { lp++;  hdr.ncnt++; }

		s = hdr.note[hdr.ncnt];
		len = strlen(s);
		max = LEN_NOTE - len;
		strim(lp);
		if (*lp) {
			if (len)  s[len++] = ' ' ;
			if (snprintf(s + len, max, "%s", lp) >= max)  strcpy(s + max - 4, "...") ;
		} else if (first) {
			hdr.ncnt--;
		}

	} else if (hstate == stAuth) {
		bool  eol = false; 
		while (!eol) {
			while (isspace(*lp)) lp++ ;
			for (cp = lp;  *cp && (*cp != ';');  cp++) ;
			if (!*cp)  eol = true ;
			while (isspace(*cp)) cp-- ;
			if (!eol)  cp[0] = '\0' ;

			if (*lp) {
				if (snprintf(hdr.auth[hdr.acnt], LEN_AUTH, "%s", lp) >= LEN_AUTH)
					strcpy(&hdr.auth[hdr.acnt][LEN_AUTH - 4], "...");
				hdr.acnt++;
			}

			lp = cp + 2;
		}

	}

_return:
	return;
}

//+=============================================================================
void  hdrReset (void)
{
	hstate = stHeader;

	*hdr.func = '\0';
	*hdr.desc = '\0';

	memset(hdr.argQual, 0, MAX_ARG * LEN_AQUAL);
	memset(hdr.argType, 0, MAX_ARG * LEN_ATYPE);
	memset(hdr.argName, 0, MAX_ARG * LEN_ANAME);
	memset(hdr.argDesc, 0, MAX_ARG * LEN_ADESC);
	hdr.argc  = 0;

	memset(hdr.note, 0, MAX_NOTE * LEN_NOTE);
	hdr.ncnt  = -1;

	memset(hdr.auth, 0, MAX_AUTH * LEN_AUTH);
	hdr.acnt  = 0;

	return;
}

//+=============================================================================
void  hdrDump (void)
{
	hdr.ncnt++;

	hdrOutput();

	hdrReset();

	return;
}

//+=============================================================================
void  freename(void)
{
	free(fname);
}

//+=============================================================================
int  process( int depth, char* path, char* rpath, char* name,
              struct stat* stat, bool dir, void* pArg )
{
	char    l[MAX_L];
	int     lcnt;
	char**  fspec;

	State  fState = stCode;

	FILE   *fh;

	LOGF(">>>%s%s<<<\n", path, name);

	if (dir)  return 0 ;

	// Linux (BASh) has a habit of globbing for us
	// Either way, this will deal with filename matching
	for (fspec = (char**)pArg;  *fspec && !glob(*fspec, name);  fspec++) ;
	if (!*fspec)  return 0 ;

	if (!fname)  atexit(freename) ;
	fname = realloc(fname, strlen(path) + strlen(name) + 1);
	sprintf(fname, "%s%s", path, name);

	if (snprintf(hdr.path, LEN_FNAME, "%s", path) >= LEN_FNAME)
		strcpy(hdr.path, &fname[strlen(path) - LEN_FNAME + 1]);
	if (snprintf(hdr.file, LEN_FNAME, "%s", name) >= LEN_FNAME)
		strcpy(hdr.file, &fname[strlen(name) - LEN_FNAME + 1]);

	fh = fopen(fname, "r");
	rewind(fh);
	lcnt = 0;

	hdrReset();

	while (!feof(fh)) {
		freadl(fh, l, MAX_L);  // Read a line from the file
		lcnt++;                // Count *all* lines
		strim(l);              // Trim prefixing and trailing whitespace
		if (!*l)  continue ;   // Ignore blank lines

		// Currently in a header?
		if (fState == stHeader) {

			// A comment line?
			if ((l[0] == '/') && (l[1] == '/')) {
				if (l[2] == '+') {  // consecutive headers?
					hdrDump();  
					hdr.line = lcnt; 
				} else {            // Collect header info
					hdrAdd(l) ;  
				}

			// Line of code
			} else {
				hdrDump();
				fState = stCode;
			}

		// Start of a header?
		} else if ((l[0] == '/') && (l[1] == '/') && (l[2] == '+'))  {
			hdr.line = lcnt;
			fState = stHeader ;
		}
	}

	fclose(fh);

	return 0;
}

//+=============================================================================
int  main(int argc,  char** argv)
{

	bool  recurse = false;
	int   idx     = 1;
	char  *root;
	char  **pattern;

	if (argc < 1+2) {
		printf("Use: %s [-s] root pattern\n", argv[0]);
		return 1;
	}

	if (strcasecmp(argv[idx], "-s") == 0) {
		recurse = true;
		idx++;
	}
	root = argv[idx++];
	pattern = &argv[idx];

	dir(root, recurse, process, pattern);

	return 0;
}
